﻿/****************************************************************************************************************************
 *  ilya lehrman (ilya@inworksllc.com)
 *  March 2011
 *  v1.1
 *  http://sp2010metadataimport.codeplex.com/
 * **************************************************************************************************************************/
using System;
using System.Linq;
using FileHelpers;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Taxonomy;
using UpdateTaxonomy.Properties;
using System.Text;
using System.Collections.Generic;

namespace UpdateTaxonomy
{
    /// <summary>
    /// This program uses FileHelpers http://filehelpers.com. FileHelpers.com is covered by the lgpl license.
    /// you can see this license at http://www.opensource.org/licenses/lgpl-license.php
    /// </summary>
    class Program
    {
        private static int _termCounter = 0;
        private static TermStore termStore;
        private static TermSet termSet;
        private static IEnumerable<ManagedMetadataEntity> firstLevelTerms;
        private static IEnumerable<ManagedMetadataEntity> secondLevelTerms;
        private static IEnumerable<ManagedMetadataEntity> thirdLevelTerms;
        private static IEnumerable<ManagedMetadataEntity> fourthLevelTerms;
        private static IEnumerable<ManagedMetadataEntity> fifthLevelTerms;
        private static IEnumerable<ManagedMetadataEntity> sixthLevelTerms;
        private static IEnumerable<ManagedMetadataEntity> seventhLevelTerms;


        static void Main(string[] args)
        {

            string csvPath = string.Empty;
            string siteUrl = string.Empty;
            string managedMetadataServiceName = string.Empty;
            string termGroupName = string.Empty;

            SetupConsoleDisplay();

            DisplayHeaderText();

            if (4 != args.Length)
            {
                DisplayEnterArgumentsText();

                if (Console.ReadKey(true).Key.ToString().Equals("N")) return;

                Console.WriteLine("");
                
                csvPath = CollectUserData(Resources.promptCSVFile);
                siteUrl = CollectUserData(Resources.promptSiteURL);
                managedMetadataServiceName = CollectUserData(Resources.promptManagedMetadataServiceName);
                termGroupName = CollectUserData(Resources.promptTermGroupName);
            }

            if (string.IsNullOrEmpty(csvPath)) csvPath = args[0];
            if (string.IsNullOrEmpty(siteUrl)) siteUrl = args[1];
            if (string.IsNullOrEmpty(managedMetadataServiceName)) managedMetadataServiceName = args[2];
            if (string.IsNullOrEmpty(termGroupName)) termGroupName = args[3];

            Console.WriteLine(csvPath);
            Console.WriteLine(siteUrl);
            Console.WriteLine(managedMetadataServiceName);
            Console.WriteLine(termGroupName);
            
            var engine = new FileHelperEngine(typeof(ManagedMetadataEntity));
            ManagedMetadataEntity[] entries = (ManagedMetadataEntity[])engine.ReadFile(csvPath);


            using (var site = new SPSite(siteUrl))
            {
                #region set up taxonomy connection
                var taxonomySession = new TaxonomySession(site);

                if (null == taxonomySession.TermStores || 0 == taxonomySession.TermStores.Count)
                {
                    Console.ForegroundColor = ConsoleColor.Red;

                    Console.WriteLine(Resources.errorCouldNotFindAnyTermStores, siteUrl);
                    return;
                }

                termStore = taxonomySession.TermStores[managedMetadataServiceName];

                if (null == termStore)
                {
                    Console.ForegroundColor = ConsoleColor.Red;

                    Console.WriteLine(Resources.errorCouldNotFindManagedService, managedMetadataServiceName);
                    return;
                }

                Group termGroup;
                if (0 == termStore.Groups.Count(group => group.Name == termGroupName))
                {
                    Console.ForegroundColor = ConsoleColor.Red;

                    Console.WriteLine(Resources.errorCouldNotFindTermGroup, termGroupName);
                    return;
                }
                else
                {
                    termGroup = termStore.Groups[termGroupName];
                }
                #endregion

                var termSetEntities = from e in entries where e.TermSetName != String.Empty select e;
                var termSetCount = termSetEntities.Count();

                //  Not a valid limit if we are doing multiple languages on import..
                //
                //if (1 != termSetCount)
                //{
                //    Console.WriteLine(Resources.errorTermSetCount, termSetCount);
                //    Console.ReadLine();
                //    return;
                //}
                
                foreach (var termSetEntity in termSetEntities)
                {
                    //TermSet termSet;
                    
                    if (0 != termGroup.TermSets.Count<TermSet>(t => t.Id == nGuid(termSetEntity.GuidString))) // Look up termset by GUID, instead of by name which may vary by language
                    {
                        termSet = termGroup.TermSets[nGuid(termSetEntity.GuidString)];
                        
                        //if term set exisits... but the LCID is different, set the working Language, and set the name.
                        if (0 == termStore.GetTermSets(termSetEntity.TermSetName, nLCID(termSetEntity.LCID)).Count)
                        {
                            termStore.WorkingLanguage = nLCID(termSetEntity.LCID); //set the working language to the language we want to add..
                            termSet.Name = termSetEntity.TermSetName; //Set the language specific name
                            termSet.Description = termSetEntity.TermSetDescription; //Set the langauge secific description (may not be supported in SP2010)
                        }
                        else { 
                            var consoleColor = Console.ForegroundColor;
                            Console.ForegroundColor = ConsoleColor.Red;
                            Console.WriteLine(Resources.infoTermSetAlreadyExists, termSetEntity.TermSetName);
                            Console.ForegroundColor = consoleColor;
                        }
                    }
                    else
                    {
                        termSet = termGroup.CreateTermSet(termSetEntity.TermSetName,
                            String.IsNullOrEmpty(termSetEntity.GuidString) ? Guid.NewGuid() : new Guid(termSetEntity.GuidString),
                            String.IsNullOrEmpty(termSetEntity.LCID) ? 1033 : int.Parse(termSetEntity.LCID));
                        termSet.Description = termSetEntity.TermSetDescription;
                    }

                    termStore.CommitAll();
                    
                    #region First Level terms Query
                    //TODO: add logic to identify parent term set.. and modify spreadsheet appropriately...
                    firstLevelTerms = from e in entries
                                      where (!String.IsNullOrEmpty(e.Level1Term) && String.IsNullOrEmpty(e.Level2Term)
                                              && String.IsNullOrEmpty(e.Level3Term) && String.IsNullOrEmpty(e.Level4Term)
                                              && String.IsNullOrEmpty(e.Level5Term) && string.IsNullOrEmpty(e.Level6Term)
                                              && String.IsNullOrEmpty(e.Level7Term) && e.LCID == termSetEntity.LCID)
                                      select e;
                    #endregion

                    //Set custom order to match import 
                    //TODO: Add logic for terms with different termsets in same file.
                    termSet.CustomSortOrder = String.Join(":", firstLevelTerms.Select(x => x.GuidString).ToArray());
                    termStore.CommitAll();
                    
                    
                    #region Second Level Terms Query
                    secondLevelTerms = from e in entries
                                       where (!String.IsNullOrEmpty(e.Level1Term) && !String.IsNullOrEmpty(e.Level2Term)
                                               && String.IsNullOrEmpty(e.Level3Term) && String.IsNullOrEmpty(e.Level4Term)
                                               && String.IsNullOrEmpty(e.Level5Term) && string.IsNullOrEmpty(e.Level6Term)
                                               && String.IsNullOrEmpty(e.Level7Term) && e.LCID == termSetEntity.LCID)
                                       select e;

                    #endregion

                    #region Third Level Terms Query
                    thirdLevelTerms = from e in entries
                                      where (!String.IsNullOrEmpty(e.Level1Term) && !String.IsNullOrEmpty(e.Level2Term)
                                              && !String.IsNullOrEmpty(e.Level3Term) && String.IsNullOrEmpty(e.Level4Term)
                                              && String.IsNullOrEmpty(e.Level5Term) && string.IsNullOrEmpty(e.Level6Term)
                                              && String.IsNullOrEmpty(e.Level7Term) && e.LCID == termSetEntity.LCID)
                                      select e; 
                    #endregion

                    #region Fourth Level Terms Query
                    fourthLevelTerms = from e in entries
                                       where (!String.IsNullOrEmpty(e.Level1Term) && !String.IsNullOrEmpty(e.Level2Term)
                                               && !String.IsNullOrEmpty(e.Level3Term) && !String.IsNullOrEmpty(e.Level4Term)
                                               && String.IsNullOrEmpty(e.Level5Term) && String.IsNullOrEmpty(e.Level6Term)
                                               && String.IsNullOrEmpty(e.Level7Term) && e.LCID == termSetEntity.LCID)
                                       select e; 
                    #endregion

                    #region Fifth Level Terms Query
                    fifthLevelTerms = from e in entries
                                      where (!String.IsNullOrEmpty(e.Level1Term) && !String.IsNullOrEmpty(e.Level2Term)
                                              && !String.IsNullOrEmpty(e.Level3Term) && !String.IsNullOrEmpty(e.Level4Term)
                                              && !String.IsNullOrEmpty(e.Level5Term) && String.IsNullOrEmpty(e.Level6Term)
                                              && String.IsNullOrEmpty(e.Level7Term) && e.LCID == termSetEntity.LCID)
                                      select e; 
                    #endregion

                    #region Sixth Level Terms Query
                    sixthLevelTerms = from e in entries
                                      where (!String.IsNullOrEmpty(e.Level1Term) && !String.IsNullOrEmpty(e.Level2Term)
                                              && !String.IsNullOrEmpty(e.Level3Term) && !String.IsNullOrEmpty(e.Level4Term)
                                              && !String.IsNullOrEmpty(e.Level5Term) && !String.IsNullOrEmpty(e.Level6Term)
                                              && String.IsNullOrEmpty(e.Level7Term) && e.LCID == termSetEntity.LCID)
                                      select e; 
                    #endregion

                    #region Seventh Level Terms Query
                    seventhLevelTerms = from e in entries
                                        where (!String.IsNullOrEmpty(e.Level1Term) && !String.IsNullOrEmpty(e.Level2Term)
                                                && !String.IsNullOrEmpty(e.Level3Term) && !String.IsNullOrEmpty(e.Level4Term)
                                                && !String.IsNullOrEmpty(e.Level5Term) && !String.IsNullOrEmpty(e.Level6Term)
                                                && !String.IsNullOrEmpty(e.Level7Term) && e.LCID == termSetEntity.LCID)
                                        select e; 
                    #endregion

                    //first level
                    foreach (var termEntity in firstLevelTerms)
                    {
                        updateTerm(termSet, termEntity, 1);

                    }//End 1st.

                    
                }

                Console.WriteLine(Resources.infoCreatedXTerms, _termCounter);
            }

        }

        /// <summary>
        /// Collects the data required to run the utility.
        /// </summary>
        /// <param name="promptString">The prompt string.</param>
        /// <returns></returns>
        private static string CollectUserData(string promptString)
        {

            string userData;
            do
            {
                Console.Write(promptString);
                Console.ForegroundColor = ConsoleColor.Green;
                userData = Console.ReadLine();

            } while (String.IsNullOrEmpty(userData));

            Console.ForegroundColor = ConsoleColor.Gray;
            Console.WriteLine("\tYou entered: {0}", userData);
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine();

            return userData;
        }

        /// <summary>
        /// Formats the console display colors, etc.
        /// </summary>
        private static void SetupConsoleDisplay()
        {
            Console.BackgroundColor = ConsoleColor.DarkMagenta;
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.BufferHeight = 3000;
            Console.Title = Resources.infoConsoleTitle;

            Console.Clear();
        }

        private static Int32 nLCID(string stLCID)
        {
            Int32 _lcid;
            if (!Int32.TryParse(stLCID, out _lcid))
                { _lcid =1033;}
            return _lcid;
        }
        private static Guid nGuid(string stGuid) 
        {
            Guid _guid;
            try 
            {
                _guid = new Guid(stGuid);
            }
            catch {
                _guid = Guid.NewGuid();
            }
            return _guid;
        }
        private static string getTermLevel(ManagedMetadataEntity entity, int termLevel)
        { 
            switch(termLevel)
            {
                case 1:
                    return entity.Level1Term.Trim();
                case 2:
                    return entity.Level2Term.Trim();
                case 3:
                    return entity.Level3Term.Trim();
                case 4:
                    return entity.Level4Term.Trim();
                case 5:
                    return entity.Level5Term.Trim();
                case 6:
                    return entity.Level6Term.Trim();
                case 7:
                    return entity.Level7Term.Trim();
                default:
                    return "no term for level: " + termLevel.ToString();

            }
        }
        private static IEnumerable<ManagedMetadataEntity> getLevelTerms(int termLevel)
        {
            switch (termLevel)
            {
                case 1:
                    return firstLevelTerms;
                case 2:
                    return secondLevelTerms;
                case 3:
                    return thirdLevelTerms;
                case 4:
                    return fourthLevelTerms;
                case 5:
                    return fifthLevelTerms;
                case 6:
                    return sixthLevelTerms;
                case 7:
                    return seventhLevelTerms;
                default:
                    return null;

            }
        }
        private static string tabs(int termLevel)
        { StringBuilder sb= new StringBuilder();
            for (int i = 1; i<=termLevel; i++)
            { sb.Append("\t"); }
            return sb.ToString();
        }

        private static void updateTerm(TermSetItem parent, ManagedMetadataEntity entity, int termLevel )
        {
            Term term;
            Guid termGuid = nGuid(entity.GuidString);

            string termName;
            if ( nLCID(entity.LCID) == 1033)
            {
                term = termSet.GetTerm(termGuid); //if it exists update it
                if (term == null)
                {
                    term = parent.CreateTerm(getTermLevel(entity, termLevel), nLCID(entity.LCID), termGuid);
                }
                termName = term.Name;
                term.SetDescription(entity.TermDescription, nLCID(entity.LCID));
                Console.WriteLine(tabs(termLevel)+"{0}", termName);
            }
            else
            {
                term = termSet.GetTerm(termGuid);
                termName = getTermLevel(entity,termLevel);
                //if label exists, update it.. else create one.
                LabelCollection allLabels = term.GetAllLabels(nLCID(entity.LCID));
                if (allLabels.Count == 0 || !allLabels.Select(x => x.Value).ToArray().Contains(TaxonomyItem.NormalizeName(termName)))
                {
                    term.CreateLabel(termName, nLCID(entity.LCID), true);
                }
                
                term.SetDescription(entity.TermDescription, nLCID(entity.LCID));
                
                Console.WriteLine(tabs(termLevel) + "{0}", termName);
            }
            try
            {
                termStore.CommitAll();
                _termCounter += 1;
            }
            catch (TermStoreOperationException ex)
            {
                var consoleColor = Console.ForegroundColor;
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine(Resources.errorUnableToSaveTerm, termName, ex.Message);
                Console.ForegroundColor = consoleColor;
                 //this breaks the whole loop, so all other level ones stop working.
            }
            
            //for each child update terms
            IEnumerable<ManagedMetadataEntity> childList = getLevelTerms(termLevel+1).Where<ManagedMetadataEntity>(t2 => getTermLevel(t2,termLevel) == getTermLevel(entity,termLevel));
            foreach (var childEntity in childList)
                { updateTerm(term, childEntity, termLevel + 1); }

            //Update prefered sort order as well
            term.CustomSortOrder = String.Join(":", childList.Select(x => x.GuidString).ToArray());
            termStore.CommitAll();
        }

        /// <summary>
        /// Displays the "enter arguments" text.
        /// </summary>
        private static void DisplayEnterArgumentsText()
        {
            Console.WriteLine();
            Console.WriteLine("I'm expecting several parameters specifying CSV path, managed metadata ");
            Console.WriteLine("service name, site url, and term group name to use for import.");
            Console.WriteLine();
            Console.WriteLine("Since i didn't get all of those parameters, do you want to enter them now? ");
            Console.WriteLine();
            Console.WriteLine("Enter N to try again from the command line or any other key to proceed. ");
            Console.WriteLine();
            Console.CursorVisible = true;
            Console.CursorSize = 10;
        }

        /// <summary>
        /// Displays the header text.
        /// </summary>
        private static void DisplayHeaderText()
        {
            Console.WriteLine("     ******************************************************************");
            Console.WriteLine("     *        - SharePoint 2010 Managed Metadata Import Tool -        *");
            Console.WriteLine("     *                          March-2011                            *");
            Console.WriteLine("     *                                                                *");
            Console.WriteLine("     *                             v1.1                               *");
            Console.WriteLine("     *                                                                *");
            Console.WriteLine("     *     FileHelpers library from FileHelpers.com is used under     *");
            Console.WriteLine("     *          the LGPL license (http://bit.ly/lgpl-license)         *");
            Console.WriteLine("     *                                                                *");
            Console.WriteLine("     *                          Ilya Lehrman                          *");
            Console.WriteLine("     *                       ilya@inworksllc.com                      *");
            Console.WriteLine("     *                                                                *");
            Console.WriteLine("     *               http://sp2010metadataimport.codeplex.com         *");
            Console.WriteLine("     ******************************************************************");
            Console.WriteLine();
        }
    }
}
